/*
 * Copyright (c) 2007 T.Cogghe, D.Leroux, S.Lévignac, S.Rives
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "Astre.hh"
#include "TeteChercheuse.hh"

double Astre::Identite::_tol=.01;

Astre::Identite::Identite(Astre&a) : Id(_tol) {
	double lum;
	double distance,dx,dy;
	int nbPix=0;
	TeteChercheuse t(TAILLE_CARRE_ASTRE-1);
	luminositeTotale=0;
	repartition=0;
	r=g=b=0;
	while(!t.aFini()) {
		if(a.masque((*t).x,(*t).y)) {
			++nbPix;
			r+=a.pic(a.org_masque.x+(*t).x,a.org_masque.y+(*t).y)[0]*(1./255.);
			g+=a.pic(a.org_masque.x+(*t).x,a.org_masque.y+(*t).y)[1]*(1./255.);
			b+=a.pic(a.org_masque.x+(*t).x,a.org_masque.y+(*t).y)[2]*(1./255.);
			lum=Image::lumd(a.pic(a.org_masque.x+(*t).x,a.org_masque.y+(*t).y))*(1./255.);
			luminositeTotale+=lum;
			/* FIXME : métrique ? */
			dx=a.org_masque.x+(*t).x-a.org_x;
			dy=a.org_masque.y+(*t).y-a.org_y;
			//distance=(dx>0?dx:-dx)+(dy>0?dy:-dy);
			distance=dx*dx+dy*dy;
			if(distance>1) {
				repartition+=1/distance;
			} else {
				repartition+=1;
			}
		}
		t.suivant();
	}
/*	if(luminositeTotale) {
		repartition*=((double)nbPix)/luminositeTotale;
	}
*/
//	r*=nbPix/luminositeTotale;
//	g*=nbPix/luminositeTotale;
//	b*=nbPix/luminositeTotale;

	r=log(1+r*r);
	g=log(1+g*g);
	b=log(1+b*b);

//	cout<<"Identité de l'astre centré en ("<<a.org_x<<','<<a.org_y<<") :"<<endl;
//	cout<<" * teinte = "<<r<<", "<<g<<", "<<b<<endl;
//	cout<<" * luminosité Totale = "<<luminositeTotale<<endl;
//	cout<<" * répartition = "<<repartition<<endl;
}




void Astre::determinerCentre() {
	TeteChercheuse t(TAILLE_CARRE_ASTRE-1);
	unsigned int lum;
	int px,py;	/* coordonnées dans l'image */
	double acculum=0;	/* accum lum */
	double accux=0,accuy=0;	/* accum pos */
	/* on parcourt tout le masque. pour chaque pixel marqué, on ajoute xy*lum et lum aux acccumulateurs */
	while(!t.aFini()) {
		if(masque((*t).x,(*t).y)) {
			px=org_masque.x+(*t).x;
			py=org_masque.y+(*t).y;
			lum=Image::lum(pic(px,py));

			acculum+=lum;
			accux+=px*lum;
			accuy+=py*lum;
		}
		t.suivant();
	}
	acculum=1./acculum;
	org_x=accux*acculum;
	org_y=accuy*acculum;

}



void Astre::delimiterObjet(int seuil) {
	TeteChercheuse t(TAILLE_CARRE_ASTRE-1);
	Position& pos=t.posTourPrec();	/* initialise la référence, on n'aura plus besoin d'affecter pos.* */

	ctr_xmin=ctr_xmax=ctr_ymin=ctr_ymax=0;

	masque(0,0,1);	/* le centre du masque est forcément à 1, puisqu'il y a un point au dessus du seuil dessous, puisqu'on a appelé le constructeur de l'astre... :) */

	//contour(0,0,0);	/* et donc forcément le contour est à 0... */

	t.suivant();

	/* tant que le point dans cette direction a été marqué au tour précédent, faire */
	while(!t.aFini()) {
		/* arme le masque si le pixel à pos courante > au seuil et 
		 * que le masque au tour précédent a été armé
		 */
		masque((*t).x,(*t).y,	pic.pixSup(org_masque.x+(*t).x,org_masque.y+(*t).y,seuil)
					  &&
					(masque(pos.x,pos.y)));

		if(masque(pos.x,pos.y)&&!masque((*t).x,(*t).y)) {
			/* on marque la limite */
			if((*t).x<ctr_xmin) ctr_xmin=(*t).x;
			if((*t).x>ctr_xmax) ctr_xmax=(*t).x;
			if((*t).y<ctr_ymin) ctr_ymin=(*t).y;
			if((*t).y>ctr_ymax) ctr_ymax=(*t).y;
		}

		t.suivant();
		/* t.posTourPrec() met implicitement pos à jour */
		t.posTourPrec();
	}

	ctr_xmin+=-1+org_masque.x;
	ctr_ymin+=-1+org_masque.y;
	ctr_ymax+=1+org_masque.y;
	ctr_xmax+=1+org_masque.x;

/*
	cout<<TAILLE_CARRE_ASTRE<<'\t'<<MOITIE_CARRE_ASTRE<<endl;
	cout<<"============================================="<<endl;
	for(int i=-16;i<16;i++) {
		for(int j=-16;j<16;j++) {
			cout<<(masque(j,i)?'*':'.');
		}
		cout<<endl;
	}
	cout<<"============================================="<<endl;
//*/
}

void Astre::tracerContour(Image&copie,const int incr) const {
	unsigned char*pix;

	for(int y=ctr_ymin;y<=ctr_ymax;y+=incr) {
		pix=copie(ctr_xmin,y); if(pix) { pix[0]=0; pix[1]=pix[2]=0xFF; }
		pix=copie(ctr_xmax,y); if(pix) { pix[0]=0; pix[1]=pix[2]=0xFF; }
	}
	for(int x=ctr_xmin;x<=ctr_xmax;x+=incr) {
		pix=copie(x,ctr_ymin); if(pix) { pix[0]=0; pix[1]=pix[2]=0xFF; }
		pix=copie(x,ctr_ymax); if(pix) { pix[0]=0; pix[1]=pix[2]=0xFF; }
	}
}



void Astre::effacer(Image&copie) const {
	TeteChercheuse t(TAILLE_CARRE_ASTRE-1);
	int px,py;		/* coordonnées dans l'image */
	unsigned char*pix;	/* pointeur sur un pixel */
	while(!t.aFini()) {
		if(masque((*t).x,(*t).y)) {
			px=org_masque.x+(*t).x;
			py=org_masque.y+(*t).y;
			pix=copie(px,py);
			if(pix!=Image::black) {
				pix[2]=pix[1]=pix[0]=0;
			}
		}
		t.suivant();
	}
}


Astre*Astre::trouverAutourDe(Image& pic,int x,int y,int seuil) {
	TeteChercheuse t(TAILLE_CARRE_ASTRE-1);
	Position pos;

	/* tronque le rectangle de l'image d'une longueur de carré,
	 * de toutes façons il est fortement déconseillé de choisir
	 * des astres sur un bord de l'image
	 */
	if(x<(int)MOITIE_CARRE_ASTRE+1) x=MOITIE_CARRE_ASTRE+1;
	else if(x>=(int)(pic.largeur()-MOITIE_CARRE_ASTRE)) x=pic.largeur()-MOITIE_CARRE_ASTRE-1;
	if(y<(int)MOITIE_CARRE_ASTRE) y=MOITIE_CARRE_ASTRE;
	else if(y>=(int)(pic.hauteur()-MOITIE_CARRE_ASTRE)) y=pic.hauteur()-MOITIE_CARRE_ASTRE-1;


	while(pic.pixInf( x+(*t).x, y+(*t).y, seuil )&&!t.aFini()) {
		t.suivant();
	}
	if(t.aFini()) {
		cout<<"TeteChercheuse n'a pas trouvé"<<endl;
		pos.x=pos.y=-1;
		return NULL;
	}

	pos.x=x+(*t).x;
	pos.y=y+(*t).y;

	return new Astre(pic,pos,seuil);
}

/* on va chercher des trucs ressemblant à aiguille dans meuleDeFoin qui est une copie de source, et on les range dans sac */
int Astre::trouverDansRectangle(const Image& source,const Astre*aiguille,int x,int y,int l,int h,const int seuil,multimap<double,Astre*>& sac) {
	Astre*a;
	double delta;
	Position pos;

	/* l'avènement du brute-force */

	/* d'abord, on sauvegarde l'image, et on triturera la copie */
	Image meuleDeFoin(source.copiePixbuf());
	sac.clear();

	/* il faut s'assurer qu'on ne risque rien pour la détection des astres, on se ménage une bordure suffisante */
	if(x<(int)MOITIE_CARRE_ASTRE) x=MOITIE_CARRE_ASTRE;
	if(x+l>=(int)(meuleDeFoin.largeur()-MOITIE_CARRE_ASTRE)) l=meuleDeFoin.largeur()-x-MOITIE_CARRE_ASTRE-1;
	if(y<(int)MOITIE_CARRE_ASTRE) y=MOITIE_CARRE_ASTRE;
	if(y+h>=(int)(meuleDeFoin.hauteur()-MOITIE_CARRE_ASTRE)) h=meuleDeFoin.hauteur()-y-MOITIE_CARRE_ASTRE-1;

	/* ensuite, on parcourt tout le rectangle */
	for(pos.y=y;pos.y<y+h;pos.y++) {
		for(pos.x=x;pos.x<x+l;pos.x++) {
			if(meuleDeFoin.pixSup( pos.x, pos.y, seuil )) {
				/* pour chaque astre trouvé dans la copie, on le trie, et on l'efface */
				//cout<<"\t\tA trouvé un astre vers "<<pos.x<<','<<pos.y<<endl;
				a=new Astre(source,pos,seuil);
				delta=a->id()%aiguille->id();
				if(delta<100.) {	/* FIXME : imaginer un vrai seuil sensé */
					sac.insert(pair<double,Astre*>(a->id()%aiguille->id(),a));
				} else {
					delete a;
				}
				a->effacer(meuleDeFoin);
			}
		}
	}
	return sac.size();
}


